package gugga.tasks.containers.taskManager
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	
	import gugga.common.BaseEnum;
	import gugga.eventHelpers.EventDescriptor;
	import gugga.eventHelpers.Listener;
	import gugga.tasks.ITask;
	import gugga.tasks.PreconditionsTask;
	import gugga.tasks.events.TaskEvent;
	import gugga.tasks.helpers.PreconditionsManager;

	[Event(name="start", type="TaskEvent")]
	[Event(name="interrupted", type="TaskEvent")]
	[Event(name="completed", type="TaskEvent")]
	
	/**
	 * This is an internal class used only by the <code>TaskManager</code> class. 
	 * <code>TaskManagerItem</code> is a task, composed by two tasks:
	 * <ul>
	 * 	<li><code>PreconditionsTask</code> encapsulating all preconditions needed for starting the scheduled task</li>
	 * 	<li><code>ScheduledTask</code> an arbitrary task that will start, only if all preconditions are met, and the <code>PreconditionsTask</code> is completed</li>
	 * </ul>
	 * @author Barni
	 */
	public class TaskManagerItem extends EventDispatcher implements ITask
	{
 		private var mScheduledTask : ITask;
		private var mPreconditions : PreconditionsTask;
		public var mState : TaskManagerItemStates;
		private var mScheduledTaskCompletedListener : Listener;
		
		public function get ScheduledTask() : ITask
		{
			return mScheduledTask;
		}
		
		public function set ScheduledTask(aValue : ITask) : void
		{
			mScheduledTaskCompletedListener.stop();
			mScheduledTask = aValue;
			
			mScheduledTaskCompletedListener = Listener.createSingleTimeListener(
				new EventDescriptor(mScheduledTask, "completed"), 
				onScheduledTaskCompleted);
			
			if(mState == TaskManagerItemStates.Running)
			{
				mScheduledTask.start();		
			}
		}
		
		public function get ImmediatelyInterruptable() : Boolean
		{
			return isImmediatelyInterruptable();
		}
		
		public function TaskManagerItem(aScheduledTask : ITask, aAccepting : Boolean)
		{
			mScheduledTask = aScheduledTask;
					
			mScheduledTaskCompletedListener = Listener.createSingleTimeListener(
				new EventDescriptor(mScheduledTask, "completed"), onScheduledTaskCompleted);		
			
			mPreconditions = new PreconditionsTask(new PreconditionsManager(aAccepting));
			mPreconditions.addEventListener("completed", onPreconditionsMet);
			
			mState = TaskManagerItemStates.Pending;
		}
		
		public function addPrecondition(aPrecondition : EventDescriptor) : void
		{
			mPreconditions.add(aPrecondition);
		}
		
		public function removePrecondition(aPrecondition : EventDescriptor) : void
		{
			mPreconditions.remove(aPrecondition);
		}
		
		public function removePreconditionsByEventSource(aEventSource : IEventDispatcher) : void
		{
			mPreconditions.removeByEventSource(aEventSource);
		}
		
		public function replacePreconditionsEventSource(
			aEventSource : IEventDispatcher, 
			aNewEventSource : IEventDispatcher) : void
		{
			mPreconditions.replaceEventSource(aEventSource, aNewEventSource);
		}
		
		public function acceptPrecondition(aPrecondition : EventDescriptor) : void
		{
			mPreconditions.accept(aPrecondition);
		}
		
		public function acceptPreconditionsByEventSource(aEventSource : IEventDispatcher) : void
		{
			mPreconditions.acceptEventSource(aEventSource);
		}
		
		public function acceptAllPreconditions() : void
		{
			mPreconditions.acceptAll();
		}
		
		public function ignorePrecondition(aPrecondition : EventDescriptor) : void
		{
			mPreconditions.ignore(aPrecondition);
		}
		
		public function ignorePreconditionsByEventSource(aEventSource : IEventDispatcher) : void
		{
			mPreconditions.ignoreEventSource(aEventSource);
		}
		
		public function ignoreAllPreconditions() : void
		{
			mPreconditions.ignoreAll();
		}
		
		/**
		 * @see gugga.tasks.PreconditionsManager#reset()
		 */
		public function reset() : void
		{
			mState = TaskManagerItemStates.Pending;
			mScheduledTaskCompletedListener.stop();
			mPreconditions.reset();
		}	
		
		public function start() : void 
		{
			if(mState != TaskManagerItemStates.Pending)
			{
				reset();
			}
			
			mPreconditions.start();
		}
	
		/**
		 * TODO: Should be implemented.
		 */
		public function isRunning() : Boolean 
		{
			return false;
		}
		
		/**
		 * <code>isImmediatelyInterruptable()</code> always returns <code>true</code> 
		 * when <code>TaskManagerItem</code> is not in <code>TaskManagerItemStates.Running</code> 
		 * state. When <code>TaskManagerItem</code> is in <code>TaskManagerItemStates.Running</code> 
		 * state, <code>isImmediatelyInterruptable()</code> returns <code>true</code> 
		 * only if the scheduled task is immediately interruptable.
		 */
		public function isImmediatelyInterruptable() : Boolean 
		{
			var result : Boolean = true;
			if(mState == TaskManagerItemStates.Running)
			{
				result = mScheduledTask.isImmediatelyInterruptable();
			}
			return result;	
		}
	
		/**
		 * TODO: Should examine case when changing ScheduledTask while interrupting
		 * <p>
		 * <code>interrupt()</code> will check whether the 
		 * <code>TaskManagerItem</code> state is <code>TaskManagerItemStates.Running</code>.
		 * <p> 
		 * If in running state, it will check whether the scheduled task is 
		 * <code>isImmediatelyInterruptable</code> and if it isn't, it will create 
		 * a listener which is subscribed to the <b><i>interrupted</i></b> event of 
		 * the scheduled task - the <code>TaskManagerItem</code> will be 
		 * <b><i>interrupted</i></b> right after this event is caught and consumed. 
		 * If the scheduled task is <code>isImmediatelyInterruptable</code> it will 
		 * be directly interrupted, and right after it, the <code>TaskManagerItem</code> 
		 * will be also interrupted. 
		 * <p>
		 * If not in running state, it will interrupt the preconditions task, 
		 * before putting <code>TaskManagerItem</code> in interrupted state. 
		 */
		public function interrupt() : void 
		{
			if(mState == TaskManagerItemStates.Running)
			{
				mScheduledTaskCompletedListener.stop();
				if(!mScheduledTask.isImmediatelyInterruptable())
				{
					Listener.createSingleTimeListener(
						new EventDescriptor(mScheduledTask, TaskEvent.INTERRUPTED),
						onScheduledTaskInterrupted);
					mScheduledTask.interrupt();
				}
				else
				{
					mScheduledTask.interrupt();
					changeToInterruptedState();
				}
			}
			else
			{
				mPreconditions.interrupt();
				changeToInterruptedState();
			}
		}
		
		public function dispose() : void
		{
			mScheduledTaskCompletedListener.stop();
			mPreconditions.ignoreAll();
		}
		
		private function changeToInterruptedState() : void
		{
			mState = TaskManagerItemStates.Interrupted;
			dispatchEvent(new TaskEvent(TaskEvent.INTERRUPTED));
		}
	
	    private function onPreconditionsMet(aEvent : Event) : void 
		{
			mState = TaskManagerItemStates.Running;
			
			dispatchEvent(new TaskEvent(TaskEvent.START));
			
			mScheduledTaskCompletedListener.start();
			mScheduledTask.start();
		}
		
		private function onScheduledTaskCompleted(aEvent : Event) : void 
		{
			mState = TaskManagerItemStates.Completed;
			dispatchEvent(new TaskEvent(TaskEvent.COMPLETED));
		}
		
		private function onScheduledTaskInterrupted(aEvent : Event) : void 
		{
			changeToInterruptedState();
		}
	}
}

import gugga.common.BaseEnum;

class TaskManagerItemStates extends BaseEnum
{
	public static var Pending : TaskManagerItemStates = new TaskManagerItemStates("Pending", 0);
	public static var Running : TaskManagerItemStates = new TaskManagerItemStates("Running", 1);
	public static var Completed : TaskManagerItemStates = new TaskManagerItemStates("Completed", 2);
	public static var Interrupted : TaskManagerItemStates = new TaskManagerItemStates("Interrupted", 3);
	
	public function TaskManagerItemStates(aName : String, aOrderIndex : Number) 
	{
		super(aName, aOrderIndex);
	}
}